package com.tramy.text.ruler;

import android.os.Build;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.StaticLayout;
import android.text.TextPaint;
import android.text.style.AbsoluteSizeSpan;
import android.util.TypedValue;

import androidx.annotation.NonNull;

import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.WritableArray;
import com.facebook.react.module.annotations.ReactModule;
import com.facebook.react.uimanager.DisplayMetricsHolder;

@ReactModule(name = TextRulerModule.NAME)
public class TextRulerModule extends ReactContextBaseJavaModule {

    public static final String NAME = "TextRuler";

    public TextRulerModule(ReactApplicationContext reactContext) {
        super(reactContext);
    }

    @Override
    @NonNull
    public String getName() {
        return NAME;
    }

    @ReactMethod
    public void measureTexts(ReadableArray dataList,
                             Promise promise) {
        WritableArray resList = Arguments.createArray();

        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
            promise.resolve(resList);
            return;
        }

        TextPaint paint = new TextPaint();
        SpannableStringBuilder spanBuilder = new SpannableStringBuilder();

        for (int i = 0; i < dataList.size(); i++) {
            ReadableMap textData = dataList.getMap(i);

            if (textData == null
                    || !textData.hasKey("fontSize")
                    || !textData.hasKey("maxWidth")
                    || !textData.hasKey("texts")) {
                resList.pushArray(Arguments.createArray());
                continue;
            }

            ReadableArray texts = textData.getArray("texts");
            if (texts == null) {
                resList.pushArray(Arguments.createArray());
                continue;
            }

            double fontSize = textData.getDouble("fontSize");
            float width = (float) textData.getDouble("maxWidth");
            AbsoluteSizeSpan sizeSpan = new AbsoluteSizeSpan((int) toPixelFromDIP((float) fontSize));

            paint.setTextSize(toPixelFromDIP((float) fontSize));

            CustomLineHeightSpan lhSpan = null;
            if (textData.hasKey("lineHeight")) {
                double lineHeight = textData.getDouble("lineHeight");
                lhSpan = new CustomLineHeightSpan(toPixelFromDIP((float) lineHeight));
            }

            WritableArray resArray = Arguments.createArray();
            for (int j = 0; j < texts.size(); j++) {
                String text = texts.getString(j);

                if (text == null) {
                    resArray.pushDouble(-1.0);
                    continue;
                }

                spanBuilder.clear();
                spanBuilder.clearSpans();

                spanBuilder.append(text);

                spanBuilder.setSpan(sizeSpan,
                  0, text.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);

                if (lhSpan != null) {
                    spanBuilder.setSpan(lhSpan,
                      0, text.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
                }

                StaticLayout layout = StaticLayout.Builder
                    .obtain(spanBuilder, 0, text.length(), paint, (int) toPixelFromDIP(width))
                    .build();

                resArray.pushDouble(toDIPFromPixel(layout.getHeight()));
            }

            resList.pushArray(resArray);
        }

        promise.resolve(resList);
    }

    public static float toPixelFromDIP(float value) {
        return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, value, DisplayMetricsHolder.getScreenDisplayMetrics());
    }

    public static float toDIPFromPixel(float value) {
        return value / DisplayMetricsHolder.getScreenDisplayMetrics().density;
    }

}
